#include "utils/filesystem/filesystem.h"

namespace txdnn {
/* -------------------------------------------------------------- */
inline void SleepMillis(unsigned int milli_seconds) _TX_NOEXCEPT {
#if defined(_WIN32)
  ::Sleep(milli_seconds);
#else
  std::this_thread::sleep_for(std::chrono::milliseconds(milli_seconds));
#endif
}

// /* File Utils*/
// #define SEP "/"

inline bool PathExists(const std::string& fname) _TX_NOEXCEPT {
#ifdef _WIN32
  struct _stat buffer;
  return (::_stat(fname.c_str(), &buffer) == 0);
#else
  struct stat buffer;
  return (::stat(fname.c_str(), &buffer) == 0);
#endif  
}

inline int RemoveFile(const std::string& fname) _TX_NOEXCEPT {
#if defined(_WIN32)
  return ::_wremove(fname.c_str());
#else
  return std::remove(fname.c_str());
#endif  
}

inline int RenameFile(const std::string& src, const std::string& dst) _TX_NOEXCEPT {
#if defined(_WIN32)
  return ::_wrename(src.c_str(), dst.c_str());
#else
  return std::rename(src.c_str(), dst.c_str());
#endif  
}

inline size_t FileSize(FILE* f) {
  if (f == nullptr) {
    std::cout << "Failed to get size. fd is null." << std::endl;
    std::abort();
  }
  // Linux
  int fd = fileno(f);
  struct stat s;
  if (fstat(fd, &s) == 0) {
    return static_cast<size_t>(s.st_size);
  }
  std::abort();
  return 0;
}

inline bool WriteBytes(const void* ptr, const size_t n_bytes, FILE* fp) {
  return std::fwrite(ptr, 1, n_bytes, fp) == n_bytes;
}

inline bool FileSync(FILE* fp) {
  return fsync(fileno(fp)) == 0;
}

inline bool FileOpen(FILE** fp, const std::string& fname, const std::string& mode) {
  *fp = fopen(fname.c_str(), mode.c_str());
  return *fp == nullptr;
}

inline bool MkDir(const std::string& dir) {
  return mkdir(dir.c_str(), mode_t(0755)) == 0;
}

// return directory name from given path or empty string
// "dir/file"     -> "dir"
// "dir/"         -> "dir"
// "dir"          -> ""
// "dir///"       -> "dir//"
inline std::string DirName(const std::string& path) {
  auto pos = path.find_last_of(SEP);
  return pos != std::string::npos ? path.substr(0, pos) : std::string{};
}

inline bool CreateDir(const std::string& path) {
  if (PathExists(path)) { return true; }
  if (path.empty()) { return false; }

  size_t search_offset = 0;
  do {
    auto token_pos = path.find_first_of(SEP, search_offset);
    if (token_pos == std::string::npos) {
      token_pos = path.size();
    }

    auto sub_dir = path.substr(0, token_pos);
    if (!sub_dir.empty() && !PathExists(sub_dir) && !MkDir(sub_dir)) {
      return false;
    }
    search_offset = token_pos + 1;

  } while (search_offset < path.size());

  return true;
}

} // namespace txdnn